Wesbos - Type Ahead (동적 검색)


완성본

💡 앞으로 개발을 하다보면 자주 사용하게 될 것 같은 동적 검색창이다.
사실 Wes Bos 30일 챌린지를 진행하면서 근 6회차 중에 가장 어려운 챕터였던 것 같다….


로직

  1. json파일 불러오기
  2. json파일을 새로운 배열 형태로 생성 (fetch)
  3. 검색어 찾기 기능 (“찾기”)
  4. 검색어에 해당하는 cities 나타내기(display)

1. json파일 불러오기

💡 json 주소

json파일

주소에 저장돼있는 json 형식의 city 파일

1
2
3
4
//이 json 파일 주소를 const를 통해 endpoint라는 값으로 지정!

const endpoint =
'https://gist.githubusercontent.com/Miserlou/c5cd8364bf9b2420bb29/raw/2bf258763cdddd704f8ffd3ea9a3e81d25e2c6f6/cities.json';

❓ 하지만, 여기까지는 아직 주소값만 지정해준 것이기 때문에,
이 json파일을 배열로써 나타나게 해줘야 한다.

1
2
// 우선 cities라는 빈 배열을 만들어주자
const cities = [];

⭐ 그렇다면, 이제 어떻게 저 주소로부터 city 값들을 불러들여올 수 있을까?

=> fetch API를 사용하면 된다 !


fetch API

💡 fetch는 서버에 네트워크 요청을 할 수 있도록 도와주는 AJAX기반의 API다.

💡 fetch(접근하고자 하는 url, 옵션)
then.() == 호출에 성공했을 때 응답객체를 resolve

즉, then은 fetch에 대한 응답형태 및 방식이라고 보면 된다.

1
2
3
4
5
6
//우선, 나는 json 주소를 endpoint로 지정해뒀으니까 주소 대신 변수명을 넣어준다.
fetch(endpoint)
//Blob은 타입이 존재하는 바이너리 객체!
.then((Blob) => Blob.json())
//이제 이 json 데이터를 위에서 생성한 cities 배열에 push 해주면 기본적인 세팅은 끝!
.then((data) => cities.push(...data));
json1

fetch(endpoint)를 통해 Promise가 resolve됐음

json2

Blob형태의 json 값들이 배열로 반환됨


검색을 통해 해당하는 city 찾기!

❓ 일단 findMatches라는 함수를 생성할건데,
filter 조건 내에서 검색창에 입력될 글자는 매번 다를 것이므로 이를 어떻게 변수화 할지가 관건이다.
이것을 해결해줄 기능이 바로 “**RegEXP와 match()”**다.

RegExp

🔥 정규표현식 = 문자검색, 문자 대체, 문자추출에 주로 사용

new RegExp(표현식, 플래그)

flag

match()

🔥 정규표현식에 맞는 문자열을 찾아서 배열 객체**(문자열 형태)**로 반환한다.

1
2
3
4
5
6
7
8
function findMatches(wordToMatch, cities) {
return cities.filter((place) => {
// 모든 문자와 여러줄을 영어 대소문자를 구분하지 않고 받아들임
const regex = new RegExp(wordToMatch, 'gi');
// "||"은 or을 뜻한다는 점 복기!
return place.city.match(regex) || place.state.match(regex);
});
}

검색어에 해당하는 city or state 나타내기

💡 우선, 검색창과 검색결과가 보여질 html 요소를 정의해주고,
addEventListener를 통해 변화에 따른 함수 실행을 설정하자

1
2
3
4
5
6
7
// html 요소 정의
const searchInput = document.querySelector('.search');
const suggestions = document.querySelector('.suggestions');

// addEventListener 사용
searchInput.addEventListener('change', displayMatches);
searchInput.addEventListener('keyup', displayMatches);

displayMatches

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function displayMatches() {
// 검색창에 입력된 글을 변수화!
const matchArray = findMatches(this.value, cities);
const html = matchArray
.map((place) => {
// 각 값들에 대한 정의
const regex = new RegExp(this.value, 'gi');
const cityName = place.city.replace(
regex,
`<span class="hl">${this.value}</span>`
);
const stateName = place.state.replace(
regex,
`<span class="hl">${this.value}</span>`
);
return `
<li>
<span class="name">${cityName}, ${stateName}</span>
<span class="population">${place.population}</span>
</li>
`;
})
.join('');
return (suggestions.innerHTML = html);
}

최종 완성 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
const endpoint =
'https://gist.githubusercontent.com/Miserlou/c5cd8364bf9b2420bb29/raw/2bf258763cdddd704f8ffd3ea9a3e81d25e2c6f6/cities.json';

console.log(endpoint);

const cities = [];

fetch(endpoint)
.then((Blob) => Blob.json())
.then((data) => cities.push(...data));

function findMatches(wordToMatch, cities) {
return cities.filter((place) => {
const regex = new RegExp(wordToMatch, 'gi');
return place.city.match(regex) || place.state.match(regex);
});
}

function displayMatches() {
const matchArray = findMatches(this.value, cities);
const html = matchArray
.map((place) => {
const regex = new RegExp(this.value, 'gi');
const cityName = place.city.replace(
regex,
`<span class="hl">${this.value}</span>`
);
const stateName = place.state.replace(
regex,
`<span class="hl">${this.value}</span>`
);
return `
<li>
<span class="name">${cityName}, ${stateName}</span>
<span class="population">${place.population}</span>
</li>
`;
})
.join('');
return (suggestions.innerHTML = html);
}

const searchInput = document.querySelector('.search');
const suggestions = document.querySelector('.suggestions');

searchInput.addEventListener('change', displayMatches);
searchInput.addEventListener('keyup', displayMatches);

Author

Hoonjoo

Posted on

2022-01-02

Updated on

2022-02-07

Licensed under

Comments